﻿// Spun AJAX Framework
// Copyright (C) 2008 Blake Hegerle
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.CodeDom;
using Microsoft.CSharp;
using System.CodeDom.Compiler;

namespace Spun.Models.Prototyping
{
    public abstract class PrototypeFactory<T>
        where T : class
    {
        public static Type Prototype
        {
            get
            {
                lock (Monitor)
                {
                    if (prototype == null)
                        prototype = MakePrototype();
                }
                return prototype;
            }
        }

        private static Type prototype;
        private static object Monitor;

        static PrototypeFactory()
        {
            Monitor = new object();
        }

        private static Type MakePrototype()
        {
            var CodeGenerator = new CodeGenerator<T>();

            var CompileUnits = new CodeCompileUnit[] { new CodeCompileUnit() };
            CompileUnits[0].Namespaces.Add(CodeGenerator.Code);

            var Options = new CompilerParameters();

            AddAssemblyReferences(Options, CodeGenerator);

            var Results = CSharpCodeProvider.CreateProvider("C#").CompileAssemblyFromDom(Options, CompileUnits);

            if (Results.Errors.HasErrors)
                throw new CompilationError<T>(Results.Errors);

            return Results.CompiledAssembly.GetType(CodeGenerator.GeneratedTypeName);
        }

        private static void AddAssemblyReferences(CompilerParameters Options, CodeGenerator<T> CodeGenerator)
        {
            foreach (var Type in CodeGenerator.ReferencedTypes)
            {
                Uri Uri = new Uri(Type.Assembly.CodeBase);

                if (Uri.IsFile)
                    Options.ReferencedAssemblies.Add(Uri.LocalPath);
                else
                    throw new Exception("Cannot reference assembly " + Uri);
            }
        }
    }
}
